package com.chen.recruit.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.recruit.common.api.CommonResult;
import com.chen.recruit.common.constants.MessageConstants;
import com.chen.recruit.common.enums.CollegeType;
import com.chen.recruit.common.enums.CommonStatus;
import com.chen.recruit.domain.Classroom;
import com.chen.recruit.domain.User;
import com.chen.recruit.domain.dto.CollegeDto;
import com.chen.recruit.mapper.ClassroomMapper;
import com.chen.recruit.mapper.UserMapper;
import com.chen.recruit.service.CollegeService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import com.chen.recruit.domain.College;
import com.chen.recruit.mapper.CollegeMapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 院系Service业务层处理
 *
 * @author cgy
 * @date 2022-08-20
 */
@Service
@RequiredArgsConstructor
public class CollegeServiceImpl extends ServiceImpl<CollegeMapper, College> implements CollegeService {

    private final UserMapper userMapper;
    private final ClassroomMapper classroomMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult addOrUpdateCollege(College college) {
        if (checkCollegeNameUnique(college)) {
            return CommonResult.fail(MessageConstants.COLLEGE_NAME_REPEAT);
        }

        College parentCollege = baseMapper.selectById(college.getParentId());
        if (checkCollegeStructureCorrect(college.getType(), parentCollege.getType())) {
            return CommonResult.fail(MessageConstants.COLLEGE_TYPE_ERROR);
        }

        if (ObjectUtil.isNotNull(college.getId()) && college.getStatus() == CommonStatus.DISABLE) {
            boolean exists = baseMapper.exists(Wrappers.<College>lambdaQuery()
                    .eq(College::getParentId, college.getId())
                    .eq(College::getStatus, CommonStatus.ENABLE));
            if (exists) {
                return CommonResult.fail(MessageConstants.HAS_CHILDREN_COLLEGE);
            }
            if (checkCollegeHasUser(college.getId())) {
                return CommonResult.fail(MessageConstants.UPDATE_COLLEGE_STATUS_FAIL);
            }
            if (checkCollegeHasClassroom(college.getId())) {
                return CommonResult.fail(MessageConstants.UPDATE_COLLEGE_STATUS_ERROR);
            }
        }

        if (ObjectUtil.isNull(college.getId()) && !this.save(college)) {
            return CommonResult.fail();
        }

        college.setAncestors(
                ObjectUtil.isNotNull(parentCollege.getAncestors()) ?
                        parentCollege.getAncestors() + StrUtil.COMMA + college.getId() :
                        parentCollege.getId() + StrUtil.COMMA + college.getId()
        );

        return this.saveOrUpdate(college) ? CommonResult.success() : CommonResult.fail();
    }

    //FIXME 删除逻辑存在问题
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CommonResult deleteCollegeById(Integer id) {
        if (checkCollegeHasUser(id)) {
            return CommonResult.fail(MessageConstants.DELETE_COLLEGE_FAIL);
        }
        if (checkCollegeHasClassroom(id)) {
            return CommonResult.fail(MessageConstants.DELETE_COLLEGE_ERROR);
        }

        List<Integer> collegeIds = baseMapper.selectList(Wrappers.<College>lambdaQuery()
                .eq(College::getParentId, id)).stream().map(College::getId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(collegeIds)) {
            if (checkCollegeHasUser(ArrayUtil.toArray(collegeIds, Integer.class))) {
                return CommonResult.fail(MessageConstants.DELETE_COLLEGE_FAIL);
            }
            if (checkCollegeHasClassroom(ArrayUtil.toArray(collegeIds, Integer.class))) {
                return CommonResult.fail(MessageConstants.DELETE_COLLEGE_ERROR);
            }
        }

        collegeIds.add(id);

        return this.removeByIds(collegeIds) ? CommonResult.success() : CommonResult.fail();
    }

    @Override
    public List<CollegeDto> getCollegesByCondition(College college) {
        List<CollegeDto> data;

        if (StringUtils.isBlank(college.getName()) && ObjectUtil.isNull(college.getType())
                && ObjectUtil.isNull(college.getStatus())) {
            List<CollegeDto> collegeDtos = BeanUtil.copyToList(this.list(), CollegeDto.class);
            data = collegeDtos.stream().filter(collegeDto -> collegeDto.getParentId() == 0)
                    .peek(collegeDto -> collegeDto.setChildren(getChildrenColleges(collegeDto.getId(), collegeDtos)))
                    .collect(Collectors.toList());
        } else {
            List<College> colleges = baseMapper.selectList(Wrappers.<College>lambdaQuery()
                    .like(StringUtils.isNotBlank(college.getName()), College::getName, college.getName())
                    .eq(ObjectUtil.isNotNull(college.getType()), College::getType, college.getType())
                    .eq(ObjectUtil.isNotNull(college.getStatus()), College::getStatus, college.getStatus()));

            List<CollegeDto> collegeDtos = BeanUtil.copyToList(colleges, CollegeDto.class);

            for (int i = 0; i < colleges.size(); i++) {
                Integer id = colleges.get(i).getId();
                colleges.removeIf(item -> item.getParentId().equals(id));
            }

            List<CollegeDto> collegeDtoList = BeanUtil.copyToList(colleges, CollegeDto.class);

            data = collegeDtoList.stream().anyMatch(collegeDto -> collegeDto.getParentId() == 0) ?

                    collegeDtoList.stream().filter(collegeDto -> collegeDto.getParentId() == 0)
                            .peek(collegeDto -> collegeDto.setChildren(getChildrenColleges(collegeDto.getId(), collegeDtos)))
                            .collect(Collectors.toList()) :

                    collegeDtoList.stream()
                            .peek(collegeDto -> collegeDto.setChildren(getChildrenColleges(collegeDto.getId(), collegeDtos)))
                            .collect(Collectors.toList());
        }

        return data;
    }

    private boolean checkCollegeNameUnique(College college) {
        return baseMapper.exists(Wrappers.<College>lambdaQuery()
                .eq(College::getName, college.getName())
                .ne(ObjectUtil.isNotNull(college.getId()), College::getId, college.getId()));
    }

    private List<CollegeDto> getChildrenColleges(Integer collegeId, List<CollegeDto> collegeDtos) {
        return collegeDtos.stream().filter(collegeDto -> collegeDto.getParentId().equals(collegeId))
                .peek(collegeDto -> collegeDto.setChildren(getChildrenColleges(collegeDto.getId(), collegeDtos)))
                .collect(Collectors.toList());
    }

    private boolean checkCollegeHasUser(Integer... ids) {
        return userMapper.exists(Wrappers.<User>lambdaQuery().in(User::getCollegeId, ids));
    }

    private boolean checkCollegeHasClassroom(Integer... ids) {
        return classroomMapper.exists(Wrappers.<Classroom>lambdaQuery().in(Classroom::getCollegeId, ids));
    }

    private boolean checkCollegeStructureCorrect(CollegeType childrenType, CollegeType parentType) {
        switch (childrenType) {
            case BRANCH:
                return parentType != CollegeType.COLLEGE;
            case MAJOR:
                return parentType != CollegeType.BRANCH;
            case CLASS:
                return parentType != CollegeType.MAJOR;
            default:
                return true;
        }
    }

}
